/** 
 * 是否是奇数
 * 0是偶数，1是奇数
 */
function isOdd(num) {
    return num % 2 == 1;
}

class theChessBoard {
    /**
     * 棋盘的宽
     */
    width;
    /**
     * 棋盘的高
     */
    height;
    /**
     * 每个格子的大小
    */
    blockwidth;
    /**
     * 棋子半径
     */
    chessSize;
    /**
     * 棋子颜色列表
     */
    colors;
    /**
     * 当前玩家
     */
    playerId;
    /**
     * 棋盘上的棋子列表  
     * 0表示空，1表示玩家1（第0个颜色），2表示玩家2（第1个颜色）
     */
    chessBoard;
    /**
     * 画布
     */
    canvas2D;
    /**
     * 此时是否可以下棋  
     * 游戏结束后不可下棋
     */
    ableToDown;
    /**
     * 几子连接时胜利
     */
    checkBoundary;
    /**
     * 是否可以杀死被围住的棋子
     */
    canDie;
    /**
     * 是否启用重力
     */
    gravity;
    /**
     * 定时器
     */
    intervalID;
    constructor() {
        this.width = parseInt(document.getElementById("width").value);
        this.height = parseInt(document.getElementById("height").value);
        this.blockwidth = parseInt(document.getElementById("blockwidth").value);
        this.chessSize = parseInt(document.getElementById("chessSize").value);
        this.colors = document.getElementById("colors").value.split(" ");
        this.playerId = 0;
        this.ableToDown = true;
        this.checkBoundary = Math.max(0, parseInt(document.getElementById("checkBoundary").value));
        this.canDie = document.getElementById("canDie").checked;
        this.gravity = document.getElementById("gravity").checked;

        this.chessBoard = new Array(this.width).fill(0).map(() => new Array(this.height).fill(0));
        console.log(this.chessBoard);

        var canvasHtml = "<canvas id=\"canvas\" width=" + (this.width*this.blockwidth) + " height=" + (this.height*this.blockwidth) + "></canvas>";
        console.log(canvasHtml);
        document.getElementById("main").innerHTML = canvasHtml;
        var canvas = document.getElementById("canvas");
        this.canvas2D = canvas.getContext("2d");
        this.repaint();
        var This = this;
        console.log(this);
        canvas.addEventListener("click", function (e) {
            var x = Math.round(parseFloat(e.offsetX)/parseFloat(This.blockwidth)-0.5);
            var y = Math.round(parseFloat(e.offsetY)/parseFloat(This.blockwidth)-0.5);
            console.log(x, y);
            This.downChess(x, y);
        });
        // 启用重力
        if (this.gravity) {
            this.intervalID = setInterval(this.checkGravity, 1000, this);
        }
    }

    /**
     * 获取该位置的棋子值（this.chessBoard[x][y]但检测边缘）
     * @param {*} x 横坐标
     * @param {*} y 纵坐标
     * @returns 棋子值（-1为出界）
     */
    getChess(x, y) {
        if (x >= 0 && x < this.width && y >= 0 && y < this.height) {
            return this.chessBoard[x][y];
        } else {
            return -1;
        }
    }

    /**
     * 重绘
     */
    repaint() {
        // 绘制外框
        this.canvas2D.fillStyle = "white";
        this.canvas2D.fillRect(0, 0, this.width*this.blockwidth, this.height*this.blockwidth);
        this.canvas2D.fillStyle = "black";
        this.canvas2D.fillRect(this.blockwidth*0.5-3, this.blockwidth*0.5-3, (this.width-1)*this.blockwidth+6, (this.height-1)*this.blockwidth+6);
        this.canvas2D.fillStyle = "white";
        this.canvas2D.fillRect(this.blockwidth*0.5+3, this.blockwidth*0.5+3, (this.width-1)*this.blockwidth-6, (this.height-1)*this.blockwidth-6);
        this.canvas2D.fillStyle = "black";
        // 绘制棋盘线
        for (var i = 0; i < this.width-1; i++) {
            this.canvas2D.fillRect((i+0.5) * this.blockwidth, 0.5 * this.blockwidth, 1, (this.height-1) * this.blockwidth);
        }
        for (var i = 0; i < this.height-1; i++) {
            this.canvas2D.fillRect(0.5 * this.blockwidth, i * this.blockwidth+0.5 * this.blockwidth, (this.width-1)*this.blockwidth, 1);
        }
        // 绘制中心点
        if (isOdd(this.width) && isOdd(this.height)) {
            this.drawCircle(this.width/2*this.blockwidth, this.height/2*this.blockwidth, this.chessSize/2, "black");
        }
        // 绘制重要点
        var heightPaint = true;
        var widthPaint = true;
        if ((this.width-1)%3==0&&(this.height-1)%3==0) {
            for (var i = 3; i < this.width-1; i+=3) {
                for (var j = 3; j < this.height-1; j+=3) {
                    if (heightPaint&&widthPaint) {
                        this.drawCircle((i+0.5)*this.blockwidth, (j+0.5)*this.blockwidth, this.chessSize/2, "black");
                    }
                    heightPaint = !heightPaint;
                }
                widthPaint = !widthPaint;
            }
        } else if (this.width >= 12 && this.height >= 12) {
            this.drawCircle(3.5*this.blockwidth, 3.5*this.blockwidth, this.chessSize/2, "black");
            this.drawCircle(3.5*this.blockwidth, (this.height-3.5)*this.blockwidth, this.chessSize/2, "black");
            this.drawCircle((this.width-3.5)*this.blockwidth, 3.5*this.blockwidth, this.chessSize/2, "black");
            this.drawCircle((this.width-3.5)*this.blockwidth, (this.height-3.5)*this.blockwidth, this.chessSize/2, "black");
        }
        // 绘制棋子
        for (var i = 0; i < this.width; i++) {
            for (var j = 0; j < this.height; j++) {
                if (this.chessBoard[i][j] != 0) {
                    this.drawCircle((i+0.5)*this.blockwidth, (j+0.5)*this.blockwidth, this.chessSize, this.colors[this.chessBoard[i][j]-1]);
                }
            }
        }
    }

    /**
     * 画圆
     * @param {*} x x坐标
     * @param {*} y y坐标
     * @param {*} r 半径
     * @param {*} color 颜色
     */
    drawCircle (x, y, r, color) {
        this.canvas2D.fillStyle = color;
        this.canvas2D.beginPath();
        this.canvas2D.arc(x, y, r, 0, Math.PI * 2, true);
        this.canvas2D.fill();
    }

    downChess (x, y) {
        // 游戏结束时不能下棋吖
        if (!this.ableToDown) {
            return;
        }
        console.log(this.chessBoard[x][y]);
        // 没有棋子便可以下棋
        var abouts = [[x+1, y], [x-1, y], [x, y+1], [x, y-1]];
        var thisColor = this.getChess(x, y);
        if (this.chessBoard[x][y] == 0) {
            this.chessBoard[x][y] = this.playerId + 1;
            if (this.canDie) {
                console.log("alive:", this.checkAlive(x, y, []));
                if(!this.checkAlive(x, y, [])) {
                    var able = false;
                    for (var i = 0; i < abouts.length; i++) {
                        var about = this.getChess(abouts[i][0], abouts[i][1]);
                        if (about!=thisColor&&about!=0&&about!=-1) {
                            if (!this.checkAlive(abouts[i][0], abouts[i][1], [])) {
                                this.dieSpreed(abouts[i][0], abouts[i][1], true);
                                able = true;
                            }
                        }
                    }
                    if (!able) {
                        this.chessBoard[x][y] = 0;
                        return;
                    }
                }
            }
            this.drawCircle((x+0.5)*this.blockwidth, (y+0.5)*this.blockwidth, this.chessSize, this.colors[this.playerId]);
            // 五子棋规则
            if (this.checkWinAndTell(x, y)) {
                return;
            }
            // 围棋规则
            if (this.canDie) {
                for (var i = 0; i < abouts.length; i++) {
                    var about = this.getChess(abouts[i][0], abouts[i][1]);
                    if (about!=thisColor&&about!=0&&about!=-1) {
                        if (!this.checkAlive(abouts[i][0], abouts[i][1], [])) {
                            this.dieSpreed(abouts[i][0], abouts[i][1], true);
                        }
                    }
                }
            }
            this.nextPlayer();
        }
    }

    nextPlayer () {
        this.playerId = (this.playerId + 1) % this.colors.length;
    }

    dieSpreed (x, y, first) {
        var idToKill = this.chessBoard[x][y];
        this.chessBoard[x][y] = 0;
        var abouts = [[x+1, y], [x-1, y], [x, y+1], [x, y-1]];
        for (var i = 0; i < abouts.length; i++) {
            if (this.getChess(abouts[i][0], abouts[i][1]) == idToKill) {
                this.dieSpreed(abouts[i][0], abouts[i][1], false);
            }
        }
        if (first) {
            this.repaint();
        }
    }

    /**
     * 检测活不活
     * @param {*} x 横坐标
     * @param {*} y 纵坐标
     * @param {*} checkedList 已检测列表
     * @returns true表示活，false表示死
     */
    checkAlive (x, y, checkedList) {
        console.log("checkAlive", x, y);
        if (checkedList.indexOf(x + "," + y) != -1) { // 防止重复检查
            console.log("重复检查", x, y);
            return false;
        }
        checkedList.push(x + "," + y); // 添加到已检查列表
        var thisColor = this.chessBoard[x][y];
        if (thisColor == 0) {
            console.log("不是棋子", x, y);
            return true;
        }
        var abouts = [[x+1, y], [x-1, y], [x, y+1], [x, y-1]];
        for (var i = 0; i < abouts.length; i++) {
            if (this.getChess(abouts[i][0], abouts[i][1]) == 0) {
                return true;
            }
            if (this.getChess(abouts[i][0], abouts[i][1]) == thisColor) {
                if (this.checkAlive(abouts[i][0], abouts[i][1], checkedList)) {
                    return true;
                }
            }
        }
        return false;
    }

    drawLine (x1, y1, x2, y2, color) {
        this.canvas2D.strokeStyle = color;
        this.canvas2D.lineWidth = this.chessSize * 0.5;
        this.canvas2D.beginPath();
        this.canvas2D.moveTo(x1*this.blockwidth, y1*this.blockwidth);
        this.canvas2D.lineTo(x2*this.blockwidth, y2*this.blockwidth);
        this.canvas2D.stroke();
    }

    checkWin (x, y) {
        if (this.chessBoard[x][y] == 0) {
            return false;
        }
        if (this.checkBoundary <= 0) {
            return false;
        }
        if (this.checkBoundary > Math.max(this.width, this.height)) {
            return false;
        }
        var count = 0;
        var startX = x - this.checkBoundary + 1;
        var startY = y - this.checkBoundary + 1;
        var win = false;
        var thisColor = this.chessBoard[x][y];
        for (var i = -this.checkBoundary + 1; i <= this.checkBoundary - 1; i++) {
            if (this.getChess(x + i, y) == thisColor) {
                if (this.gravity && this.getChess(x + i, y + 1) == 0) {
                    continue;
                }
                count++;
                if (count >= this.checkBoundary) {
                    var endX = startX + count - 1;
                    while (this.getChess(startX, y) == thisColor) {
                        count++;
                        startX--;
                    }
                    while (this.getChess(endX, y) == thisColor) {
                        count++;
                        endX++;
                    }
                    this.drawLine(startX+1, y+0.5, endX, y+0.5, this.colors[thisColor-1]);
                    win = true;
                    break;
                }
            } else {
                count = 0;
                startX = x + i + 1;
            }
        }
        count = 0;
        startX = x - this.checkBoundary + 1;
        for (var i = -this.checkBoundary + 1; i <= this.checkBoundary - 1; i++) {
            if (this.getChess(x, y + i) == thisColor) {
                if (this.gravity && this.getChess(x, y + i) == 0) {
                    continue;
                }
                count++;
                if (count >= this.checkBoundary) {
                    var endY = startY + count - 1;
                    while (this.getChess(x, startY) == thisColor) {
                        count++;
                        startY--;
                    }
                    while (this.getChess(x, endY) == thisColor) {
                        count++;
                        endY++;
                    }
                    this.drawLine(x+0.5, startY+1, x+0.5, endY, this.colors[thisColor-1]);
                    win = true;
                    break;
                }
            } else {
                count = 0;
                startY = y + i + 1;
            }
        }
        count = 0;
        startX = x - this.checkBoundary + 1;
        startY = y - this.checkBoundary + 1;
        for (var i = -this.checkBoundary + 1; i <= this.checkBoundary - 1; i++) {
            if (this.getChess(x + i, y + i) == thisColor) {
                if (this.gravity && this.getChess(x + i, y + i) == 0) {
                    continue;
                }
                count++;
                if (count >= this.checkBoundary) {
                    var endX = startX + count - 1;
                    var endY = startY + count - 1;
                    while (this.getChess(startX, startY) == thisColor) {
                        count++;
                        startX--;
                        startY--;
                    }
                    while (this.getChess(endX, endY) == thisColor) {
                        count++;
                        endX++;
                        endY++;
                    }
                    this.drawLine(startX+1, startY+1, endX, endY, this.colors[thisColor-1]);
                    win = true;
                    break;
                }
            } else {
                count = 0;
                startX = x + i;
                startY = y + i;
            }
        }
        count = 0;
        startX = x - this.checkBoundary + 1;
        startY = y + this.checkBoundary - 1;
        for (var i = -this.checkBoundary + 1; i <= this.checkBoundary - 1; i++) {
            if (this.getChess(x + i, y - i) == thisColor) {
                if (this.gravity && this.getChess(x + i, y - i) == 0) {
                    continue;
                }
                count++;
                if (count >= this.checkBoundary) {
                    var endX = startX + count - 1;
                    var endY = startY - count + 1;
                    while (this.getChess(startX, startY) == thisColor) {
                        count++;
                        startX--;
                        startY++;
                    }
                    while (this.getChess(endX, endY) == thisColor) {
                        count++;
                        endX++;
                        endY--;
                    }
                    this.drawLine(startX+1, startY, endX, endY+1, this.colors[thisColor-1]);
                    win = true;
                    break;
                }
            } else {
                count = 0;
                startX = x + i;
                startY = y - i;
            }
        }
        return [win, thisColor];
    }

    checkWinAndTell (x, y) {
        var r = this.checkWin(x, y);
        var win = r[0];
        var color = r[1];
        console.log("check ", win, color);
        if (win) {
            alert("玩家" + color + "(颜色：" + this.colors[color-1] + ")胜利！");
            this.ableToDown = false;
            clearInterval(this.intervalID);
            return true;
        }
        return false;
    }

    checkGravity (This=this) {
        var movedList = [];
        for (var x = 0; x < This.width; x++) {
            for (var y = This.height-2; y >= 0; y--) {
                if (This.chessBoard[x][y] != 0 && This.chessBoard[x][y+1] == 0) {
                    This.chessBoard[x][y+1] = This.chessBoard[x][y];
                    This.chessBoard[x][y] = 0;
                    movedList.push([x, y]);
                    movedList.push([x, y+1]);
                }
            }
        }
        This.repaint();
        for (var i = 0; i < movedList.length; i++) {
            // 五子棋规则
            if (This.checkWinAndTell(movedList[i][0], movedList[i][1])) {
                return true;
            }
            // 围棋规则
            if (This.canDie) {
                if (movedList[i][0] - 1 >= 0) {
                    if (!This.checkAlive(movedList[i][0]-1, movedList[i][1], [])) {
                        This.dieSpreed(movedList[i][0]-1, movedList[i][1], true);
                    }
                }
                if (movedList[i][0] + 1 < This.width) {
                    if (!This.checkAlive(movedList[i][0]+1, movedList[i][1], [])) {
                        This.dieSpreed(movedList[i][0]+1, movedList[i][1], true);
                    }
                }
                if (movedList[i][1] - 1 >= 0) {
                    if (!This.checkAlive(movedList[i][0], movedList[i][1]-1, [])) {
                        This.dieSpreed(movedList[i][0], movedList[i][1]-1, true);
                    }
                }
                if (movedList[i][1] + 1 < This.height) {
                    if (!This.checkAlive(movedList[i][0], movedList[i][1]+1, [])) {
                        This.dieSpreed(movedList[i][0], movedList[i][1]+1, true);
                    }
                }
            }
        }
    }
}

var theNewestChessBoard = new theChessBoard();
function newChessBoard() {
    theNewestChessBoard =  new theChessBoard();
}
function nextPlayer() {
    theNewestChessBoard.nextPlayer();
}
